/*
 * This file is part of the OSMembrane project.
 * More informations under www.osmembrane.de
 * 
 * The project is licensed under the GNU GENERAL PUBLIC LICENSE 3.0.
 * for more details about the license see http://www.osmembrane.de/license/
 * 
 * Source: $HeadURL: http://osmembrane-gui.googlecode.com/svn/sources/src/de/osmembrane/view/dialogs/SettingsDialog.java $ ($Revision: 883 $)
 * Last changed: $Date: 2011-03-07 15:00:52 +0000 (Mon, 07 Mar 2011) $
 */

package de.osmembrane.view.dialogs;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Locale;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;

import de.osmembrane.model.settings.SettingType;
import de.osmembrane.model.settings.SettingsTypeUpdateInterval;
import de.osmembrane.tools.I18N;
import de.osmembrane.view.AbstractDialog;
import de.osmembrane.view.components.JTextFieldWithButton;
import de.osmembrane.view.interfaces.ISettingsDialog;

/**
 * The dialog containing the program's settings
 * 
 * @author tobias_kuhn
 * 
 */
public class SettingsDialog extends AbstractDialog implements ISettingsDialog {

	private static final long serialVersionUID = 6498307196575629577L;

	/**
	 * Whether or not to apply the changes made in the dialog
	 */
	private boolean applyChanges;

	/**
	 * Components to edit the Osmosis default path
	 */
	private JTextFieldWithButton osmosisPath;

	/**
	 * Components to edit the JOSM default path
	 */
	private JTextFieldWithButton josmPath;

	/**
	 * Components to edit the working path
	 */
	private JTextFieldWithButton workingPath;

	/**
	 * Components to edit the default zoom
	 */
	private JLabel defaultZoomDisplay;
	private JSlider defaultZoom;

	/**
	 * Components to edit the language / locale
	 */
	private JComboBox language;
	private Locale[] locales;

	/**
	 * Components to edit whether short task names shall be used
	 */
	private JCheckBox shortTasks;

	/**
	 * Components to edit whether default parameters shall be exported
	 */
	private JCheckBox defaultParamExport;

	/**
	 * Components to edit how much undo steps shall be available
	 */
	private JSpinner maxUndoSteps;

	/**
	 * Components to edit the grid size
	 */
	private JLabel rasterSizeDisplay;
	private JSlider rasterSize;
	private JCheckBox rasterSizeEnable;

	/**
	 * Components to edit whether the startup screen shall be displayed
	 */
	private JCheckBox showStartup;

	/**
	 * Components to edit how often updates shall be searched
	 */
	private JComboBox updateInterval;

	/**
	 * Components to edit the PLaF
	 */
	private JComboBox pLaF;

	/**
	 * Generates a new {@link SettingsDialog}.
	 */
	public SettingsDialog() {
		// set the basics up
		setLayout(new BorderLayout());

		setWindowTitle(I18N.getInstance().getString("View.SettingsDialog"));

		// control buttons
		JButton okButton = new JButton(I18N.getInstance().getString("View.OK"));
		okButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				applyChanges = true;
				hideWindow();
			}
		});

		JButton defaultButton = new JButton(I18N.getInstance().getString(
				"View.ResetDefault"));

		defaultButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// reset default values
				for (SettingType st : SettingType.values()) {
					setValue(st, st.getDefaultValue());
				}
			}
		});

		JButton cancelButton = new JButton(I18N.getInstance().getString(
				"View.Cancel"));
		cancelButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				applyChanges = false;
				hideWindow();
			}
		});

		JPanel buttonCtrlGrid = new JPanel(new GridLayout(1, 3));
		buttonCtrlGrid.add(okButton);
		buttonCtrlGrid.add(defaultButton);
		buttonCtrlGrid.add(cancelButton);

		JPanel buttonCtrlFlow = new JPanel(new FlowLayout(FlowLayout.CENTER));
		buttonCtrlFlow.add(buttonCtrlGrid);

		add(buttonCtrlFlow, BorderLayout.SOUTH);

		// actual settings
		JPanel settings = new JPanel(new GridBagLayout());
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.insets = new Insets(8, 8, 8, 8);

		final int minFieldWidth = 256;
		final int minSpinnerWidth = 64;

		ActionListener selectFiles = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				final JTextFieldWithButton jtfwb = (JTextFieldWithButton) ((JButton) e
						.getSource()).getParent();

				JFileChooser fc = new JFileChooser();
				fc.setSelectedFile(new File(jtfwb.getValue()));
				if (jtfwb.equals(workingPath)) {
					fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
					fc.setAcceptAllFileFilterUsed(false);
				}
				fc.setFileFilter(new FileFilter() {

					@Override
					public String getDescription() {
						return jtfwb.getToolTipText();
					}

					@Override
					public boolean accept(File f) {
						return (jtfwb.equals(workingPath)) ? f.isDirectory()
								: f.canExecute();
					}
				});
				if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
					jtfwb.setValue(fc.getSelectedFile().getAbsolutePath());
				}
			}
		};

		gbc.gridy = 0;
		gbc.gridx = 0;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.DEFAULT_OSMOSIS_PATH")
						+ ":"), gbc);
		gbc.gridx = 1;
		osmosisPath = new JTextFieldWithButton("", "...");
		osmosisPath.setPreferredSize(new Dimension(minFieldWidth, osmosisPath
				.getPreferredSize().height));
		osmosisPath.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.DEFAULT_OSMOSIS_PATH.Description"));
		osmosisPath.addButtonActionListener(selectFiles);
		settings.add(osmosisPath, gbc);

		gbc.gridy = 1;
		gbc.gridx = 0;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.DEFAULT_JOSM_PATH")
						+ ":"), gbc);
		gbc.gridx = 1;
		josmPath = new JTextFieldWithButton("", "...");
		josmPath.setPreferredSize(new Dimension(minFieldWidth, josmPath
				.getPreferredSize().height));
		josmPath.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.DEFAULT_JOSM_PATH.Description"));
		josmPath.addButtonActionListener(selectFiles);
		settings.add(josmPath, gbc);

		gbc.gridy = 2;
		gbc.gridx = 0;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.DEFAULT_WORKING_DIRECTORY")
						+ ":"), gbc);
		gbc.gridx = 1;
		workingPath = new JTextFieldWithButton("", "...");
		workingPath.setPreferredSize(new Dimension(minFieldWidth, workingPath
				.getPreferredSize().height));
		workingPath.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.DEFAULT_WORKING_DIRECTORY.Description"));
		workingPath.addButtonActionListener(selectFiles);
		settings.add(workingPath, gbc);

		gbc.gridy = 3;
		gbc.gridx = 0;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.ACTIVE_LANGUAGE")
						+ ":"), gbc);
		gbc.gridx = 1;
		language = new JComboBox();
		language.setPreferredSize(new Dimension(minFieldWidth, language
				.getPreferredSize().height));
		language.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.ACTIVE_LANGUAGE.Description"));
		settings.add(language, gbc);
		
		gbc.gridy = 4;
		gbc.gridx = 0;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.ACTIVE_PLAF")
						+ ":"), gbc);
		gbc.gridx = 1;
		String[] pLaFOptions = new String[UIManager.getInstalledLookAndFeels().length];
		for (int i = 0; i < UIManager.getInstalledLookAndFeels().length; i++) {
			pLaFOptions[i] = UIManager.getInstalledLookAndFeels()[i].getName();
		}
		pLaF = new JComboBox(pLaFOptions);
		pLaF.setPreferredSize(new Dimension(minFieldWidth, pLaF
				.getPreferredSize().height));
		pLaF.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.ACTIVE_PLAF.Description"));
		settings.add(pLaF, gbc);
		
		gbc.gridy = 5;
		gbc.gridx = 0;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.UPDATE_INTERVAL")
						+ ":"), gbc);
		gbc.gridx = 1;
		String[] updateOptions = {
				I18N.getInstance().getString(
						"Model.Settings.Type.UPDATE_INTERVAL.Never"),
				I18N.getInstance().getString(
						"Model.Settings.Type.UPDATE_INTERVAL.PerDay"),
				I18N.getInstance().getString(
						"Model.Settings.Type.UPDATE_INTERVAL.PerWeek") };
		updateInterval = new JComboBox(updateOptions);
		updateInterval.setPreferredSize(new Dimension(minFieldWidth,
				updateInterval.getPreferredSize().height));
		updateInterval.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.UPDATE_INTERVAL.Description"));
		settings.add(updateInterval, gbc);

		gbc.gridy = 6;
		gbc.gridx = 0;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.DEFAULT_ZOOM_SIZE")
						+ ":"), gbc);
		gbc.gridx = 1;
		defaultZoomDisplay = new JLabel("");
		settings.add(defaultZoomDisplay, gbc);

		gbc.gridy = 7;
		gbc.gridx = 0;
		gbc.gridwidth = 2;
		defaultZoom = new JSlider(1, 200);
		defaultZoom.setMajorTickSpacing(10);
		defaultZoom.setMinorTickSpacing(1);
		defaultZoom.setPaintTicks(true);
		defaultZoom.setPaintLabels(false);
		defaultZoom.setPreferredSize(new Dimension(minFieldWidth, defaultZoom
				.getPreferredSize().height));
		defaultZoom.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.DEFAULT_ZOOM_SIZE.Description"));
		defaultZoom.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				double x = defaultZoomToModel(defaultZoom.getValue());
				defaultZoomDisplay.setText(String.format("%.2fx", x));
			}
		});
		settings.add(defaultZoom, gbc);
		gbc.gridwidth = 1;

		gbc.gridy = 0;
		gbc.gridx = 2;
		gbc.gridwidth = 2;
		shortTasks = new JCheckBox(I18N.getInstance().getString(
				"Model.Settings.Type.USE_SHORT_TASK_NAMES_IF_AVAILABLE"));
		shortTasks
				.setToolTipText(I18N
						.getInstance()
						.getString(
								"Model.Settings.Type.USE_SHORT_TASK_NAMES_IF_AVAILABLE.Description"));
		settings.add(shortTasks, gbc);
		gbc.gridwidth = 1;

		gbc.gridy = 1;
		gbc.gridx = 2;
		gbc.gridwidth = 2;
		defaultParamExport = new JCheckBox(I18N.getInstance().getString(
				"Model.Settings.Type.EXPORT_PARAMETERS_WITH_DEFAULT_VALUES"));
		defaultParamExport
				.setToolTipText(I18N
						.getInstance()
						.getString(
								"Model.Settings.Type.EXPORT_PARAMETERS_WITH_DEFAULT_VALUES.Description"));
		settings.add(defaultParamExport, gbc);
		gbc.gridwidth = 1;

		gbc.gridy = 2;
		gbc.gridx = 2;
		gbc.gridwidth = 2;
		showStartup = new JCheckBox(I18N.getInstance().getString(
				"Model.Settings.Type.SHOW_STARTUP_SCREEN"));
		showStartup.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.SHOW_STARTUP_SCREEN.Description"));
		settings.add(showStartup, gbc);
		gbc.gridwidth = 1;

		gbc.gridy = 3;
		gbc.gridx = 2;
		settings.add(
				new JLabel(I18N.getInstance().getString(
						"Model.Settings.Type.MAXIMUM_UNDO_STEPS")
						+ ":"), gbc);
		gbc.gridx = 3;
		maxUndoSteps = new JSpinner();
		maxUndoSteps.setPreferredSize(new Dimension(minSpinnerWidth,
				maxUndoSteps.getPreferredSize().height));
		maxUndoSteps.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.MAXIMUM_UNDO_STEPS.Description"));
		settings.add(maxUndoSteps, gbc);

		gbc.gridy = 6;
		gbc.gridx = 2;
		rasterSizeEnable = new JCheckBox(I18N.getInstance().getString(
				"Model.Settings.Type.PIPELINE_RASTER_SIZE")
				+ ":");
		rasterSizeEnable.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				rasterSize.setEnabled(rasterSizeEnable.isSelected());
			}
		});
		settings.add(rasterSizeEnable, gbc);
		gbc.gridx = 3;
		rasterSizeDisplay = new JLabel("");
		settings.add(rasterSizeDisplay, gbc);

		gbc.gridy = 7;
		gbc.gridx = 2;
		gbc.gridwidth = 2;
		rasterSize = new JSlider(1, 100);
		rasterSize.setMajorTickSpacing(10);
		rasterSize.setMinorTickSpacing(1);
		rasterSize.setPaintTicks(true);
		rasterSize.setPaintLabels(false);
		rasterSize.setSnapToTicks(true);
		rasterSize.setToolTipText(I18N.getInstance().getString(
				"Model.Settings.Type.PIPELINE_RASTER_SIZE.Description"));
		rasterSize.addChangeListener(new ChangeListener() {

			@Override
			public void stateChanged(ChangeEvent e) {
				int x = rasterSizeToModel(rasterSize.getValue());
				rasterSizeDisplay.setText(String.valueOf(x) + " px");
			}
		});
		settings.add(rasterSize, gbc);
		gbc.gridwidth = 1;

		add(settings, BorderLayout.CENTER);

		pack();
		centerWindow();
	}

	@Override
	public boolean shallApplyChanges() {
		return this.applyChanges;
	}

	@Override
	public void setLocales(Locale[] locales) {
		this.locales = locales;
		String[] localeStrings = new String[locales.length];
		for (int i = 0; i < locales.length; i++) {
			localeStrings[i] = locales[i].getDisplayLanguage();
		}
		language.setModel(new DefaultComboBoxModel(localeStrings));
	}

	/**
	 * Translates a JSlider value in [1,200] to a modelValue of (0,10].
	 * 
	 * @param viewValue
	 *            the slider position in [1,200]
	 * @return the modelValue in (0,2]
	 */
	private double defaultZoomToModel(int viewValue) {
		return (double) viewValue / 100.0;
	}

	/**
	 * Translates a modelValue of (0,2] to a JSlider value in [1,200].
	 * 
	 * @param modelValue
	 *            the modelValue in (0,2]
	 * @return the slider position in [0,200]
	 */
	private int defaultZoomFromModel(double modelValue) {
		return (int) (100.0 * modelValue);
	}

	/**
	 * Translates a JSlider value of [0,100] to a model value in [0,500]
	 * 
	 * @param viewValue
	 *            JSlider value in [0,100]
	 * @return the the modelValue in [0,500]
	 */
	private int rasterSizeToModel(int viewValue) {
		if (!rasterSizeEnable.isSelected()) {
			return 0;
		} else {
			return 5 * rasterSize.getValue();
		}
	}

	/**
	 * Translates a modelValue of [0,500] to a JSlider value in [0,100]
	 * 
	 * @param viewValue
	 *            the modelValue in [0,500]
	 * @return the JSlider value in [0,100]
	 */
	private int rasterSizeFromModel(int modelValue) {
		return modelValue / 5;
	}

	@Override
	public Object getValue(SettingType type) {
		switch (type) {
		case DEFAULT_OSMOSIS_PATH:
			return this.osmosisPath.getValue();

		case DEFAULT_JOSM_PATH:
			return this.josmPath.getValue();

		case DEFAULT_WORKING_DIRECTORY:
			return this.workingPath.getValue();

		case ACTIVE_LANGUAGE:
			int selected = this.language.getSelectedIndex();
			return (selected >= 0) ? this.locales[this.language
					.getSelectedIndex()] : null;

		case DEFAULT_ZOOM_SIZE:
			int x = defaultZoom.getValue();
			return defaultZoomToModel(x);

		case USE_SHORT_TASK_NAMES_IF_AVAILABLE:
			return this.shortTasks.isSelected();

		case EXPORT_PARAMETERS_WITH_DEFAULT_VALUES:
			return this.defaultParamExport.isSelected();

		case MAXIMUM_UNDO_STEPS:
			return this.maxUndoSteps.getValue();

		case PIPELINE_RASTER_SIZE:
			int y = this.rasterSize.getValue();
			return rasterSizeToModel(y);

		case SHOW_STARTUP_SCREEN:
			return this.showStartup.isSelected();

		case UPDATE_INTERVAL:
			switch (this.updateInterval.getSelectedIndex()) {
			case 1:
				return SettingsTypeUpdateInterval.ONCE_A_DAY;
			case 2:
				return SettingsTypeUpdateInterval.ONCE_A_WEEK;
			default:
				return SettingsTypeUpdateInterval.NEVER;
			}

		case ACTIVE_PLAF:
			return this.pLaF.getSelectedItem();

		default:
			return null;
		}
	}

	@Override
	public void setValue(SettingType type, Object value) {
		switch (type) {
		case DEFAULT_OSMOSIS_PATH:
			this.osmosisPath.setValue((String) value);
			break;

		case DEFAULT_JOSM_PATH:
			this.josmPath.setValue((String) value);
			break;

		case DEFAULT_WORKING_DIRECTORY:
			this.workingPath.setValue((String) value);
			break;

		case ACTIVE_LANGUAGE:
			Locale newLocale = (Locale) value;
			for (int i = 0; i < locales.length; i++) {
				if (locales[i].equals(newLocale)) {
					this.language.setSelectedIndex(i);
					return;
				}
			}
			this.language.setSelectedIndex(-1);
			break;

		case DEFAULT_ZOOM_SIZE:
			double x = (Double) value;
			this.defaultZoom.setValue(0);
			this.defaultZoom.setValue(defaultZoomFromModel(x));
			break;

		case USE_SHORT_TASK_NAMES_IF_AVAILABLE:
			this.shortTasks.setSelected((Boolean) value);
			break;

		case EXPORT_PARAMETERS_WITH_DEFAULT_VALUES:
			this.defaultParamExport.setSelected((Boolean) value);
			break;

		case MAXIMUM_UNDO_STEPS:
			this.maxUndoSteps.setValue((Integer) value);
			break;

		case PIPELINE_RASTER_SIZE:
			int rasterSize = rasterSizeFromModel((Integer) value);
			this.rasterSizeEnable.setSelected(rasterSize != 0);
			this.rasterSize.setEnabled(rasterSize != 0);
			this.rasterSize.setValue(0);
			this.rasterSize.setValue(rasterSize);
			break;

		case SHOW_STARTUP_SCREEN:
			this.showStartup.setSelected((Boolean) value);
			break;

		case UPDATE_INTERVAL:
			SettingsTypeUpdateInterval stui = (SettingsTypeUpdateInterval) value;
			int newIndex = -1;
			switch (stui) {
			case NEVER:
				newIndex = 0;
				break;
			case ONCE_A_DAY:
				newIndex = 1;
				break;
			case ONCE_A_WEEK:
				newIndex = 2;
				break;
			}
			this.updateInterval.setSelectedIndex(newIndex);
			break;

		case ACTIVE_PLAF:
			this.pLaF.setSelectedItem((String) value);

		}
	}

}
